import java.util.*;
import java.util.Map.Entry;

/**
 * This is the data structure in charged of storing tasks/events.
 * 
 * @author caixin89
 * 
 */
public class DataStruc {
	public enum DATE_TYPE {
		start, end
	};

	private TreeMap<Date, Task> startTime, endTime;

	/**
	 * Constructor that initializes 2 TreeMaps (1 to sort according to startTime and the other according
	 * to endTime)
	 */
	public DataStruc() {
		startTime = new TreeMap<Date, Task>();
		endTime = new TreeMap<Date, Task>();
	}

	/**
	 * This operation deletes a task/event.
	 * 
	 * @param x
	 */
	public void delete(Task x) {
		if (x.getClass().getName() == "Event")
			endTime.remove(((Event) x).EndTime);

		startTime.remove(x.StartTime);
	}

	/**
	 * This operation searches for tasks/events with a certain name.
	 * 
	 * @param x
	 * @return Vector containing tasks/events matching field
	 */
	public Vector<Task> search(String x) {
		Vector<Task> answer = new Vector<Task>();
		for (Map.Entry<Date, Task> entry : startTime.entrySet()) {
			Task val = entry.getValue();
			if (val.name.contains(x))
				answer.add(val);
		}
		return answer;
	}

	/**
	 * This operation searches for a task/event with a certain Date.
	 * 
	 * @param type
	 * @param x
	 * @return
	 */
	public Task search(DATE_TYPE type, Date x) {
		if (type == DATE_TYPE.start)
			return startTime.get(x);

		else
			return endTime.get(x);
	}

	/**
	 * This operation adds a task/event.
	 * 
	 * @param x
	 * @return Whether operation is successful
	 */
	public boolean add(Task x) {
		if (startTime.containsKey(x.StartTime))
			return false;

		if (x.getClass().getName() == "Event") {
			Event temp = (Event) x;
			Entry<Date, Task> compare1, compare2;

			if (endTime.containsKey(temp.EndTime))
				return false;

			else {
				compare1 = endTime.higherEntry(temp.StartTime);
				Date temp2;

				if (compare1 != null
						&& temp.EndTime
								.after(((Event) compare1.getValue()).EndTime))
					return false;

				compare2 = startTime.lowerEntry(temp.EndTime);

				while (compare2 != null
						&& compare2.getValue().getClass().getName() != "Event") {
					temp2 = compare2.getKey();
					compare2 = startTime.lowerEntry(temp2);
				}

				if (compare2 != null
						&& temp.StartTime.before(compare2.getValue().StartTime))
					return false;

			}
			endTime.put(temp.EndTime, temp);
		}

		startTime.put(x.StartTime, x);
		return true;
	}

	/**
	 * Marks a task/event as done.
	 * 
	 * @param x
	 */
	public void mark(Task x) {
		x.done = true;
	}

	/**
	 * This operation edits a task/event's details. Use if there are no date
	 * changes.
	 * 
	 * @param old_Task
	 * @param new_priority
	 *            (Pass in -1 if no change)
	 * @param new_name
	 *            (Pass in null if no change)
	 */
	public void edit(Task old_Task, int new_priority, String new_name) {
		if (new_priority != -1)
			old_Task.priority = new_priority;

		if (new_name != null)
			old_Task.name = new_name;
	}

	/**
	 * This operation edits a task/event's details. Use if there are date
	 * changes.
	 * 
	 * @param old_Task
	 * @param new_StartTime
	 * @param new_EndTime
	 *            (Pass in null if not event)
	 * @param new_priority
	 *            (Pass in -1 if no change)
	 * @param new_name
	 *            (Pass in null if no change)
	 */
	public void edit(Task old_Task, Date new_StartTime, Date new_EndTime,
			int new_priority, String new_name) {
		Task new_Task;
		if (new_priority == -1)
			new_priority = old_Task.priority;

		if (new_name == null)
			new_name = old_Task.name;

		if (new_EndTime == null) {
			new_Task = new Task(new_priority, new_StartTime, new_name,
					old_Task.done);
		}

		else {
			new_Task = new Event(new_priority, new_StartTime, new_EndTime,
					new_name, old_Task.done);
			Event temp = (Event) old_Task;
			endTime.remove(temp.EndTime);
		}

		startTime.remove(old_Task.StartTime);
		add(new_Task);
	}

	/**
	 * This operation returns a map from START, inclusive, to END
	 * @param type (DATE_TYPE.start to search according to startTime, DATE_TYPE.end to search according to endTime)
	 * @param START
	 * @param END
	 * @return
	 */
	public Map<Date, Task> search(DATE_TYPE type, Date START, Date END) {
		if (type == DATE_TYPE.start)
			return startTime.subMap(START, END);
		else
			return endTime.subMap(START, END);

	}
	
	/**
	 * This operation returns a sorted set containing a key-value entry.
	 * Use an iterator to access the elements
	 * 
	 * --Sample code---
	 * Iterator<Entry<Date, Task>> it = listAll().iterator();
	 * while(it.hasNext()) {
	 * 	Task temp = it.next().getValue();
	 * 	//process the task here
	 * }
	 * @return
	 */
	public Set<Entry<Date, Task>> listAll() {
		return startTime.entrySet();
	}
}